qx.Class.define("lastfmbackup.model.Sync", {
  
  extend : qx.core.Object,
  
  properties : {
    
    storageModel : {
      check        : "qx.core.Object",
      deferredInit : true
    },
    
    fetchModel : {
      check        : "qx.core.Object",
      deferredInit : true,
      apply        : "_applyFetchModel"
    },
    
    requestDelay : {
      check : "Number",
      init  : 100
    },
    
    progress : {
      check : "Number",
      init  : null,
      event : "changeProgress"
    },
    
    progressType : {
      check    : "String",
      nullable : true,
      event    : "changeProgressType"
    }
    
  },
  
  events : {

    /**
     *  Fired when backup is complete
     */
    complete : "qx.event.type.Event",
    
    /**
     *  Fired when model is prepared
     */
    prepared : "qx.event.type.Data",
    
    /**
     *  Fired when sync is aborted
     */
    abort : "qx.event.type.Event"
    
  },
  
  members : {
    
    _pagesToLoad  : null,
    _currentPage  : null,
    _aborted      : null,
    
    
    _onLoadCount : function(event)
    {
      var local  = this.getStorageModel().getStats();
      var remote = event.getData().data;
      if(!local)
      {
        this._pagesToLoad = remote.totalPages;
        this._currentPage = this._pagesToLoad;
        
        this.fireDataEvent("prepared", remote.total);
      }
      else if(remote.total > local.scrobbles)
      {
        var newTracks = remote.total - local.scrobbles;
        
        this._pagesToLoad = Math.floor(newTracks / remote.perPage) + (newTracks % remote.perPage ? 1 : 0);
        this._currentPage = this._pagesToLoad;
        
        this.fireDataEvent("prepared", newTracks);
      }
      else
      {
        this.fireDataEvent("prepared", 0);
      }
    },
    
    _onLoadPage : function(event)
    {
      var data = event.getData().data;
      this.getStorageModel().appendData(data);
      this.setProgress((this._pagesToLoad - this._currentPage + 1) / this._pagesToLoad);
      
      if(this._currentPage > 1)
      {
        qx.event.Timer.once(function()
        {
          this.getFetchModel().page(--this._currentPage);
        }, this, this.getRequestDelay());
      }
      else
      {
        this.setProgressType("save");
        var bindingId = this.getStorageModel().bind("saveProgress", this, "progress");
        this.getStorageModel().addListenerOnce("save", function()
        {
          this.getStorageModel().removeBinding(bindingId);
          this.fireEvent("complete");
        }, this);
        this.getStorageModel().flushData();
      }
    },
    
    _onLoad : function(event)
    {
      if(this._aborted)
      {
        this._onFail();
      }
      else
      {
        this["_onLoad" + qx.lang.String.capitalize(event.getData().type)](event);
      }
    },
    
    _onFail : function()
    {
      this._aborted = true;
      var bindingId = this.getStorageModel().bind("saveProgress", this, "progress");
      this.getStorageModel().addListenerOnce("save", function()
      {
        this.getStorageModel().removeBinding(bindingId);
        this.fireEvent("abort");
      }, this);
      this.getStorageModel().flushData();
    },
    
    abort : function()
    {
      this._aborted = true;
    },
    
    _applyFetchModel : function(value, old)
    {
      if(old)
      {
        old.removeListener("load", this._onLoad, this);
        old.removeListener("fail", this._onFail, this);
      }

      value.addListener("load", this._onLoad, this);
      value.addListener("fail", this._onFail, this);
    },
    
    prepare : function()
    {
      this._aborted = false;
      this.resetProgress();
      this.resetProgressType();
      this.getFetchModel().count();
    },
    
    run : function()
    {
      this._aborted = false;
      if(!this._currentPage)
      {
        throw new Error("Nothing to load, was model prepared?"); 
      }
      
      this.setProgressType("load");
      this.setProgress(0);
      this.getFetchModel().page(this._currentPage);
    }
    
  }
  
});